package com.myc.subject.infra.basic.service.impl;

import com.myc.subject.common.entity.PageResult;
import com.myc.subject.common.enums.SubjectInfoTypeEnum;
import com.myc.subject.infra.basic.entity.EsSubjectFields;
import com.myc.subject.infra.basic.entity.SubjectInfoEs;
import com.myc.subject.infra.basic.es.EsIndexInfo;
import com.myc.subject.infra.basic.es.EsRestClient;
import com.myc.subject.infra.basic.es.EsSearchRequest;
import com.myc.subject.infra.basic.es.EsSourceData;
import com.myc.subject.infra.basic.service.SubjectEsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

@Service
@Slf4j
public class SubjectEsServiceImpl implements SubjectEsService {


    @Override
    public boolean insert(SubjectInfoEs subjectInfoEs) {
        //构造EsSourceData，放入docID，与数据map
        EsSourceData esSourceData = new EsSourceData();
        Map<String, Object> map = convert2EsSourceData(subjectInfoEs);
        esSourceData.setDocId(subjectInfoEs.getDocId().toString());
        esSourceData.setData(map);
        //执行封装好的新增文档方法
        EsRestClient.insertDoc(getEsIndexInfo(),esSourceData);
        return false;
    }

    /***
     * 获取数据map
     */
    private Map<String, Object> convert2EsSourceData(SubjectInfoEs subjectInfoEs) {
        Map<String,Object> data = new HashMap<>();
        data.put(EsSubjectFields.SUBJECT_ID, subjectInfoEs.getSubjectId());
        data.put(EsSubjectFields.DOC_ID, subjectInfoEs.getDocId());
        data.put(EsSubjectFields.SUBJECT_NAME, subjectInfoEs.getSubjectName());
        data.put(EsSubjectFields.SUBJECT_ANSWER, subjectInfoEs.getSubjectAnswer());
        data.put(EsSubjectFields.SUBJECT_TYPE, subjectInfoEs.getSubjectType());
        data.put(EsSubjectFields.CREATE_USER, subjectInfoEs.getCreateUser());
        data.put(EsSubjectFields.CREATE_TIME, subjectInfoEs.getCreateTime());
        return data;
    }

    /***
     * EsIndexInfo类数据获取
     */
    private EsIndexInfo getEsIndexInfo(){
        EsIndexInfo esIndexInfo = new EsIndexInfo();
        esIndexInfo.setClusterName("4e90ebf56b4c");
        esIndexInfo.setIndexName("subject_index");
        return esIndexInfo;
    }

    @Override
    public PageResult<SubjectInfoEs> querySubjectList(SubjectInfoEs req) {
        //构造分页数据
        PageResult<SubjectInfoEs> pageResult = new PageResult<>();
        //构造es数据
        EsSearchRequest esSearchRequest = createSearchList(req);
        //执行查询获得数据
        SearchResponse searchResponse = EsRestClient.searchWithTermQuery(getEsIndexInfo(), esSearchRequest);
        //转化成SubjectInfoEs
        List<SubjectInfoEs> subjectInfoEsList = new LinkedList<>();
          //拿到所有命中的数据
        SearchHits searchHits = searchResponse.getHits();
          //校验若是完全没命中
        if(searchHits == null || searchHits.getHits() == null){
            //啥也没有直接返回
            pageResult.setPageNo(req.getPageNo());
            pageResult.setPageSize(req.getPageSize());
            pageResult.setRecords(subjectInfoEsList);
            pageResult.setTotal(0);
            return pageResult;
        }
          //能拿到东西则进行处理
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            //将SearchHit[]中的数据挨个转化成SubjectInfoEs
            SubjectInfoEs subjectInfoEs = conventResult(hit);
            //不为空则插入到列表中
            if (Objects.nonNull(subjectInfoEs)){
                subjectInfoEsList.add(subjectInfoEs);
            }
        }
        pageResult.setPageNo(req.getPageNo());
        pageResult.setPageSize(req.getPageSize());
        pageResult.setRecords(subjectInfoEsList);
        pageResult.setTotal(Long.valueOf(searchHits.getTotalHits().value).intValue());
        return pageResult;
    }

    /***
     * 转化hit为SubjectInfoEs
     * @param hit
     * @return
     */
    private SubjectInfoEs conventResult(SearchHit hit) {
        //数据在hit中的位置
        Map<String, Object> sourceAsMap = hit.getSourceAsMap();
        //校验是否为空
        if (CollectionUtils.isEmpty(sourceAsMap)){
            return null;
        }
        //不为空则进行转化
        SubjectInfoEs result = new SubjectInfoEs();
        result.setSubjectId(MapUtils.getLong(sourceAsMap, EsSubjectFields.SUBJECT_ID));
        result.setSubjectName(MapUtils.getString(sourceAsMap, EsSubjectFields.SUBJECT_NAME));
        result.setSubjectAnswer(MapUtils.getString(sourceAsMap, EsSubjectFields.SUBJECT_ANSWER));
        result.setDocId(MapUtils.getLong(sourceAsMap, EsSubjectFields.DOC_ID));
        result.setSubjectType(MapUtils.getInteger(sourceAsMap, EsSubjectFields.SUBJECT_TYPE));
        result.setScore(new BigDecimal(String.valueOf(hit.getScore())).multiply(new BigDecimal(100).
                setScale(2, RoundingMode.HALF_UP)));
        //处理name的高亮
        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
        HighlightField subjectNameHighlightField = highlightFields.get(EsSubjectFields.SUBJECT_NAME);
        if (Objects.nonNull(subjectNameHighlightField)){
            //获取段落
            Text[] fragments = subjectNameHighlightField.getFragments();
            //定义字符串
            StringBuilder subjectNameBuilder = new StringBuilder();
            //循环拼接
            for (Text fragment : fragments) {
                subjectNameBuilder.append(fragment);
            }
            result.setSubjectName(subjectNameBuilder.toString());
        }
        //处理答案的高亮
        HighlightField subjectAnswerField = highlightFields.get(EsSubjectFields.SUBJECT_ANSWER);
        if (Objects.nonNull(subjectAnswerField)){
            Text[] fragments = subjectAnswerField.getFragments();
            StringBuilder subjectAnswerBuilder = new StringBuilder();
            for (Text fragment : fragments) {
                subjectAnswerBuilder.append(fragment);
            }
            result.setSubjectAnswer(subjectAnswerBuilder.toString());
        }

        return result;
    }

    //构造查询条件查询
    private EsSearchRequest createSearchList(SubjectInfoEs req) {
        //查询
        EsSearchRequest esSearchRequest = new EsSearchRequest();
        //构造查询的模糊条件
        BoolQueryBuilder bq = new BoolQueryBuilder();
        MatchQueryBuilder subjectNameQueryBuilder = QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_NAME, req.getKeyWord());
        //添加按照subjectName的模糊条件，shoold是应该，不必须
        bq.should(subjectNameQueryBuilder);
        //增加查询权重
        subjectNameQueryBuilder.boost(2);

        MatchQueryBuilder subjectAnswerQueryBuilder = QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_ANSWER, req.getKeyWord());
        bq.should(subjectAnswerQueryBuilder);

        MatchQueryBuilder subjectTypeQueryBuilder = QueryBuilders.matchQuery(EsSubjectFields.SUBJECT_TYPE, SubjectInfoTypeEnum.BRIEF.getCode());
        //必须有
        bq.must(subjectTypeQueryBuilder);
        //定义bq至少要匹配一个shoold条件
        bq.minimumShouldMatch(1);

        //构造高亮数据
        HighlightBuilder highlightBuilder = new HighlightBuilder().field("*").requireFieldMatch(false);
        highlightBuilder.preTags("<span style = \"color:red \">");
        highlightBuilder.postTags("</span>");

        //整个的条件传值
        esSearchRequest.setBq(bq);
        esSearchRequest.setHighlightBuilder(highlightBuilder);
        esSearchRequest.setFields(EsSubjectFields.FIELD_QUERY);
        esSearchRequest.setFrom((req.getPageNo() - 1) * req.getPageSize());
        esSearchRequest.setSize(req.getPageSize());
        esSearchRequest.setNeedScroll(false);

        return esSearchRequest;

    }
}
